#ifndef ENTITY_H
#define ENTITY_H

#include "Core/UUID.h"
#include "Scene/Components.h"
#include "Scene/Scene.h"
//#include "entt.hpp"
#include "Core/utils.h"

namespace CC
{

    class Entity
    {
    public:
        Entity() = default;
        Entity(Scene *scene);
        Entity(const Entity &other) = default;
        ~Entity();

        void AddChild(Entity *e)
        {
            e->SetParentEasy(this);

            m_childs.push_back(e);
        }

        Entity *GetChild(int index)
        {
            CC_ASSERT(index < GetChildCount(), "ASSERT: Entity& GetChild index(%d) >= size(%d)\n", index, GetChildCount());

            return m_childs[index];
        }

        bool RemoveAllChild()
        {
            std::vector<Entity *>::iterator it = m_childs.begin();
            for (it; it != m_childs.end(); it++)
            {
                delete *it;
            }
            m_childs.clear();
            return true;
        }

        bool RemoveChild(Entity *e)
        {
            std::vector<Entity *>::iterator it = m_childs.begin();
            for (it; it != m_childs.end(); it++)
            {
                if (*e == *(*it))
                {
                    m_childs.erase(it);
                    delete e;
                    e = nullptr;
                    return true;
                }
                else
                {
                    bool r = (*it)->RemoveChild(e);
                    if (r)
                        return true;
                }
            }
            return false;
        }

        bool RemoveChildByDrag(Entity *e)
        {
            std::vector<Entity *>::iterator it = m_childs.begin();
            for (it; it != m_childs.end(); it++)
            {
                if (*e == *(*it))
                {
                    (*it)->SetParentEasy(NULL);
                    m_childs.erase(it);                   
                    return true;
                }
            }
            return false;
        }

        int GetChildCount() { return m_childs.size(); }

        void SetParent(Entity *p) { 
            if (m_parent == p) return;

            if (m_parent) {
                m_parent->RemoveChildByDrag(this);
            }

            m_parent = p;
            
            if (m_parent) {
                m_parent->AddChild(this);
            }           
           
        }
        Entity *GetParent() { return m_parent; }
        void SetParentEasy(Entity* p) { m_parent = p; }

        void AddChildByMeshes(std::vector<Mesh> &meshes)
        {
            for (int i = 0; i < (int)meshes.size(); i++)
            {
                //Entity *a = m_Scene->CreateEntity("-" + GetName() + Tool::Int2Str(i), this);
                Entity *a = m_Scene->CreateEntity(meshes[i].Name, this);

                MeshComponent* m = new MeshComponent(meshes[i]);
                m->componentType = ComponentType_Mesh;
                a->AddOrReplaceComponent<MeshComponent>(m);

                MeshModelMaterialComponent* mm = new MeshModelMaterialComponent(meshes[i].HasColorMaterial, meshes[i].ColorMaterial, meshes[i].Textures);
                mm->componentType = ComponentType_MeshModelMaterial;
                a->AddOrReplaceComponent<MeshModelMaterialComponent>(mm);
            }
        }

        template<typename T>
        Component* AddComponent(T* c)
        {
            return AddOrReplaceComponent<T>(c);
        }

        template<typename T>
        Component* AddOrReplaceComponent(T* c)
        {
            if(c == NULL) return NULL;
            RemoveComponent(c->componentType);
            m_components[c->componentType] = (Component*)c;
            m_Scene->OnComponentAdded(this, (Component*)c);
            return c;
        }

        template<typename T>
        T* GetComponent(ComponentType type) const
        {
            return dynamic_cast<T*>(m_components[type]);
        }

        bool HasComponent(ComponentType type)
        {
            if ((int)type >= m_components.size()) return false;
            return m_components[type] != NULL;
        }

        void RemoveComponent(ComponentType type)
        {
            if(!HasComponent(type)){
                return;
            }
            m_Scene->OnComponentRemoved(this, m_components[type]);
            delete m_components[type];
            m_components[type] = NULL;
        }

        // operator bool() const { return m_EntityHandle != entt::null; }
        // operator entt::entity() const { return m_EntityHandle; }
        // operator uint32_t() const { return (uint32_t)m_EntityHandle; }

        const UUID GetUUID() const { return (GetComponent<IDComponent>(ComponentType_ID))->ID; }
        const std::string &GetName() { return (GetComponent<TagComponent>(ComponentType_Tag))->Tag; }

        bool operator==(const Entity &other) const
        {
            return GetUUID() == other.GetUUID();
        }

        bool operator!=(const Entity &other) const
        {
            return GetUUID() != other.GetUUID();
        }

        void SetAnimationInfo(AnimationInfo& info) {
            m_AnimationInfo = info;
            CC_CORE_INFO("SetAnimationInfo BoneCounter=%d\n", info.BoneCounter);
        }
        AnimationInfo GetAnimationInfo() { return m_AnimationInfo; }

        void SetUserData(const std::string& id, const std::string& data);
        const std::string GetUserData(const std::string& id);

    private:
        std::vector<Component*> m_components;
        Scene *m_Scene = nullptr;
        std::vector<Entity *> m_childs;
        Entity *m_parent;

        AnimationInfo m_AnimationInfo;
        std::unordered_map<std::string, std::string> m_userdatas;
    };

}
#endif